/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/


/**
 * 首先，promise是一个类,传入一个函数作为参数，直接调用
 * promise 有三个状态， pending， fulfilled， rejected
 * 在 resolve 和 reject调用后状态修改，且状态修改后不能再修改
 * 将 resolve 和 reject 中的参数记录下来，作为 then 方法成功和失败回调的参数
 * 如果 promise 中执行出错，要捕获错误，可以使用try catch来捕获
 * 需要捕获错误的地方包括promise传入的函数执行器，和 then 方法的回调
 */
 const PENDING = 'pending'
 const FULFILLED = 'fulfilled'
 const REJECRED = 'rejected'
 class MyPromise {
   constructor(fn) {
     try {
       // promise 传入一个函数，直接调用，函数的参数为 resolve 和 reject
       fn(this.resolve, this.reject)
     } catch (err) {
       this.reject(err)
     }
   }
   // 定义初始状态
   status = PENDING
   // then 方法成功回调的参数
   value = undefined
   // then 方法失败回调的参数
   error = undefined
   // 初始化存储 then 回调的值
   sCallback = []
   fCallback = []
   resolve = (value) => {
     // 如果状态不是 pending ，不做修改
     if (this.status !== PENDING) return
     // resolve 后将状态修改为成功
     this.status = FULFILLED
     // 将结果记录
     this.value = value
     // 如果有储存的成功回调，则调用，数组需要循环调用
     // this.sCallback && this.sCallback(value)
     while (this.sCallback.length) this.sCallback.shift()()
   }
   reject = (error) => {
     // 如果状态不是 pending ，不做修改
     if (this.status !== PENDING) return
     // reject 后将状态修改为失败
     this.status = REJECRED
     // 将结果记录
     this.error = error
     // 如果有储存的失败回调，则调用，数组需要循环调用
     // this.fCallback && this.fCallback(error)
     while (this.fCallback.length) this.fCallback.shift()()
   }
   /**
    * then 方法参数为成功回调和失败回调
    * 根据状态判断执行哪个回调
    * 如果是异步调用，执行 then 方法时状态还是 pending，则要将两个回调储存起来
    * 储存的方法在 resolve 和 reject 的方法里对应的调用
    * 同一个promise可能会有多个 then 调用，也就会有多组成功和失败的回调，将异步时回调储存为数组
    * then 方法可以链式调用，所以它返回的是一个promise对象，将回调中返回的值作为下一个then方法的参数
    * then 方法返回的promise对象不能是自身,将 newPromise 与 返回值进行判断
    * 在pending状态也要判断不能返回自身
    * then 方法可以不传递参数，不传递参数时，下一个then可以拿到这个then应该拿到的结果
    * 所以 then 不传递参数时，相当于把结果传递到下一个then
    */
   then(sCallback = value => value, fCallback = error => { throw error }) {
     let newPromise = new MyPromise((resolve, reject) => {
       // 这里是同步执行，所以可以将要执行的操作放在这里
       if (this.status === FULFILLED) {
         setTimeout(() => {
           try {
             // 调用后获取返回的值
             const x = sCallback(this.value)
             // 判断返回的值如果是 promise 对象，根据promise的结果进行resolve和reject
             // 如果是普通值，直接resolve
             // 这个操作在失败是也会调用，所以包装成一个方法
             // then 方法不能返回自己，所以将 newPromise 传进去判断
             // 但是这里其实拿不到newPromise，可以将这段代码放入 setTimeout 中
             // 放入setTimeout 中并不是为了延时，只是为了等 newPromise 创建好了可以引用，所以时间设为0
             thenValue(newPromise, x, resolve, reject)
           } catch (err) {
             reject(err)
           }
         }, 0)
       } else if (this.status === REJECRED) {
         setTimeout(() => {
           try {
             const x = fCallback(this.error)
             thenValue(newPromise, x, resolve, reject)
           } catch (err) {
             reject(err)
           }
         }, 0)
       } else {
         // 调用 then 方法时，promise的异步还没执行完，状态还是pending，把两个回调储存
         // 判断不能返回自身
         this.sCallback.push(() => {
           setTimeout(() => {
             try {
               const x = sCallback(this.value)
               thenValue(newPromise, x, resolve, reject)
             } catch (err) {
               reject(err)
             }
           }, 0)
         })
         this.fCallback.push(() => {
           setTimeout(() => {
             try {
               const x = fCallback(this.error)
               thenValue(newPromise, x, resolve, reject)
             } catch (err) {
               reject(err)
             }
           }, 0)
         })
       }
     })
     return newPromise
   }
   /**
    * 实现finally方法, finally 方法不管promise成功失败都会执行回调
    * finally 会将promise的结果往下传
    * 可以利用 then 方法来实现
    * finally 方法返回一个新的promise对象,由于then方法就是返回一个promise对象，所以直接返回
    * 如果finally返回一个promise对象，要等promise对象有了结果，才会执行下方的 then
    */
   finally(callback) {
     return this.then(value => {
       return MyPromise.resolve(callback()).then(() => value)
     }, err => {
       return MyPromise.resolve(callback()).then(() => { throw err })
     })
   }
   /**
    * 实现 catch,catch方法只有一个回调，就是失败回调,返回一个promise
    */
   catch(callback) {
     return this.then(undefined, callback)
   }
   /**
    * 实现一个all方法, all 方法传入一个数组，数组中会有异步调用，返回一个新的promise对象
    * 数组中所有异步都成功，将结果以数组形式返回，否则一个出错就出错
    */
   static all(args) {
     let results = []
     let index = 0
     return new MyPromise((resolve, reject) => {
       function addData(key, value) {
         results[key] = value
         // index代表给results中添加了几个值，如果index和args长度相等，说明全部成功
         // 不能用results长度来判断，因为results赋值不是通过 push 方法，而是针对 key 来赋值的
         index++
         if (index == args.length) {
           resolve(results)
         }
       }
       for (let i = 0; i < args.length; i++) {
         // 判断是promise对象还是普通值，普通值直接加入results数组
         if (args[i] instanceof MyPromise) {
           // promise 对象
           args[i].then(value => {
             addData(i, value)
           }, reject)
         } else {
           // 普通值
           addData(i, args[i])
         }
       }
     })
   }
   /**
    * 实现一个Promise.resolve方法
    * Promise.resolve方法后面要接 then 方法
    * 参数如果是个promise对象，就按照这个promise执行，返回它
    * 参数如果是个普通值，创建一个新的promise对象
    */
   static resolve(value) {
     if (value instanceof MyPromise) return value
     return new MyPromise(resolve => { resolve(value) })
   }
   /**
    * Promise.reject 方法，返回一个新的Promise，状态为reject
    * 参数原封不动的作为reject的理由
    */
   static reject(reason) {
     return new Promise((resolve, reject) => { reject(reason) })
   }
 }
 function thenValue(newPromise, x, resolve, reject) {
   if (newPromise === x) return reject(new TypeError('then方法不能返回自己'))
   if (x instanceof MyPromise) {
     // 如果是promise对象
     x.then(resolve, reject)
   } else {
     // 如果是普通值
     resolve(x)
   }
 }
 




 